
// This file is part of Module Proxy.

// Module Proxy is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Module Proxy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Module Proxy.  If not, see <https://www.gnu.org/licenses/>.


//         Copyright (C) 2021 - 2030  关中麦客  
//         All rights reserved
//
//         mod_http.rs
//         HTTP反向代理模块
//
//         created by 关中麦客 1036038462@qq.com

use log;
use hyper::{Client, Body, Request, Response, Error, Uri};
use std::sync::Mutex;
use lazy_static::lazy_static;
use std::collections::HashMap;

//全局缓存
lazy_static! {
    //第一层key：mod_name, 第二层key：proxy_addr
    static ref PROXY: Mutex<HashMap<String, HashMap<String, u32>>> = Mutex::new(HashMap::new());
}

const ERR_TIMEOUT: u32 = 60;        //转发错误超时时间（60秒） 

/// 构建转发集群
pub fn init_cluster()
{
    let vec = super::conf::all_module_http(); //获得所有http module配置
    for one_config in vec
    {
        let mod_name = one_config.module;      //模块名
        let addr = one_config.proxy_pass;      //地址（集群配置）

        let mut cluster_map: HashMap<String, u32> = HashMap::new(); //模块名对应的转发集群队列

        let cluster: Vec<&str> = addr.split(",").collect(); //逗号分割集群地址
        for one_item in cluster
        {
            let proxy_addr = one_item.trim().to_string();    //地址（格式 http://127.0.0.1:8000）
            cluster_map.insert(proxy_addr, 0);  //压入转发集群队列
        }

        PROXY.lock().unwrap().insert(mod_name, cluster_map);   //置入缓存
    }
}

/// 判断模块名是否存在于http代理配置中
pub fn exist(mod_name: &str) -> bool
{
    if let Some(_) = PROXY.lock().unwrap().get(mod_name)
    {
        return true;
    }

    false
}

/// 从http代理集群中获得一个可转发地址
fn get_addr_from_cluster(mod_name: &str) -> String
{
    let mut ok_vec: Vec<String> = vec!{};  //可转发地址集合

    if let Some(addr_map) = PROXY.lock().unwrap().get(mod_name)
    {
        //过滤出可转发地址
        for (addr, last_err_time) in addr_map
        {
            if last_err_time + ERR_TIMEOUT < super::util::sec_timestamp() //已过超时时间
            {
                ok_vec.push(addr.clone());
            }
        } 
        
        //如果可转发地址队列空，将所有地址置入可转发队列
        if ok_vec.len() == 0
        {
            for (addr, _) in addr_map
            {
                ok_vec.push(addr.clone());
            }
        }

        //如果可转发地址队列仍然空（配置问题），返回空字符串让转发出错
        if ok_vec.len() == 0
        {
            return "".to_string();
        }

        //从可转发队列中随机选择一个
        let index = super::util::rand(0, ok_vec.len() as u32) as usize;
        return ok_vec[index].clone();
    }

    //mod_name不存在（配置文件错误）
    "".to_string()
}

/// 修改缓存
fn set_status(mod_name: String, proxy_addr: String)
{
    if let Some(addr_map) = PROXY.lock().unwrap().get_mut(&mod_name)
    {
        addr_map.insert(proxy_addr.to_string(), super::util::sec_timestamp());      //修改缓存
    }
}

/// 打印缓存
pub fn print_cache()
{
    log::debug!("[module http cluster]");
    for (mod_name, addr_map) in PROXY.lock().unwrap().iter()
    {
        for (addr, last_err_time) in addr_map
        {
            if last_err_time + ERR_TIMEOUT < super::util::sec_timestamp() //已过超时时间
            {
                log::debug!("{} : {} -- OK", mod_name, addr);
            }
            else
            {
                log::debug!("{} : {} -- REJECT", mod_name, addr);
            }
        }
    }
}

/// http反向代理
pub async fn forward(mut request: Request<Body>, mod_name: &str) -> Result<Response<Body>, Error>
{
    // 转发url，例如 http://127.0.0.1:8080
    let forward_url = get_addr_from_cluster(mod_name);  
    // 例如 /aaa/bbb/ccc?name=valu
    let path_query = request.uri().path_and_query().map(|x| x.as_str()).unwrap_or("/");

    let uri_string = format!("{}{}", forward_url, path_query);
    log::debug!("[mod_http] forward - {}",uri_string );

    //uri置入源request 
    match uri_string.parse::<hyper::Uri>()
    {
        Ok(uri) => 
        {
            *request.uri_mut() = uri;
        },
        Err(err) => 
        {
            log::error!("{} - http proxy uri error: {}", forward_url, err);
            return Ok(super::response::rsp_500().await);
        },
    }
    
    //发起客户端请求
    match client_req(request).await
    {
        Ok(response) =>
        {
            return Ok(response);
        }
        Err(err) =>
        {
            log::error!("{} - http proxy forward error: {}", forward_url, err);
            set_status(mod_name.to_string(), forward_url);  //转发错误记录到缓存
            print_cache();  //打印缓存
            return Ok(super::response::rsp_500().await);        
        }
    }
}

/// 发起一个http request请求，并接收response
pub async fn client_req(request: Request<Body>) -> Result<Response<Body>, Error>
{
    let client = Client::new();
    match client.request(request).await
    {
        Ok(response) => 
        {
            return Ok(response);
        },
        Err(_err) =>
        {
            return Ok(super::response::rsp_500().await);
        },          
    }    
}

/// 发起一个http uri请求，并接收response
pub async fn client_uri(uri: Uri) -> Result<Response<Body>, Error>
{
    let client = Client::new();
    match client.get(uri).await
    {
        Ok(response) => 
        {
            return Ok(response);
        },
        Err(_err) =>
        {
            return Ok(super::response::rsp_500().await);
        },          
    }    
}